#include <stdlib.h> 
#include <stdio.h>
#include <iostream>
#include <math.h>

using namespace std;

#include "image.h"

ImageType::ImageType()
{
 N = 0;
 M = 0;
 Q = 0;

 pixelValue = NULL;
}

ImageType::ImageType(int tmpN, int tmpM, int tmpQ)
{
 int i, j;

 N = tmpN;
 M = tmpM;
 Q = tmpQ;

 pixelValue = new int* [N];
 for(i=0; i<N; i++) {
   pixelValue[i] = new int[M];
   for(j=0; j<M; j++)
     pixelValue[i][j] = 0;
 }
}

void ImageType::getImageInfo(int& rows, int& cols, int& levels)
{
 rows = N;
 cols = M;
 levels = Q;
} 

void ImageType::setImageInfo(int rows, int cols, int levels)
{
 N= rows;
 M= cols;
 Q= levels;
} 

void ImageType::setPixelVal(int i, int j, int val)
{
 pixelValue[i][j] = val;
}

void ImageType::getPixelVal(int i, int j, int& val)
{
 val = pixelValue[i][j];
}

void ImageType :: threshold( int thresh )
{
int i;
int j; 
int val;

 for(i=0; i<N; i++)
   for(j=0; j<M; j++) {
     getPixelVal(i, j, val);
     if(val < thresh) 
       setPixelVal(i, j, 0);
     else
       setPixelVal(i, j, 255);
    }
}

void ImageType :: erode( ImageType oldImage )
{
int i;
int j;
int val,N1,N2,N3,N4,N5,N6,N7,N8;

for( i=0; i<N; i++ ) 
	for( j = 0; j<M; j++ ){
		oldImage.getPixelVal( i, j, val );
 			if( j-1 > 0 )		
				oldImage.getPixelVal( i, j-1, N1 );
			if( i-1 > 0 && j-1 > 0 )
				oldImage.getPixelVal( i-1, j-1, N2 );
			if( i-1 > 0 )
				oldImage.getPixelVal( i-1, j, N3 );
			if( i-1 > 0 && j+1 < M ) // maybe M -1 ?
  				oldImage.getPixelVal( i-1, j+1, N4 );
			if( j+1 < M )
				oldImage.getPixelVal( i, j+1, N5 );
			if( i+1 < N && j+1 < M )
				oldImage.getPixelVal( i+1, j+1, N6 );
			if( i+1 < N ) 
				oldImage.getPixelVal( i+1, j, N7 );
			if( i+1 < N && j-1 > 0 )
				oldImage.getPixelVal( i+1, j-1, N8 );
			if( N1 == 0 || N2 == 0 || N3 == 0 || N4 == 0 || N5 == 0 || N6 == 0 || N7 == 0 || N8 == 0 )
				setPixelVal( i, j, 0 );
			else
				setPixelVal( i, j, val );
	}

}  

void ImageType :: dilate( ImageType oldImage )
{
int i;
int j;
int val,N1,N2,N3,N4,N5,N6,N7,N8;

for( i=0; i<N; i++ ) 
	for( j = 0; j<M; j++ ){
		oldImage.getPixelVal( i, j, val );
 			if( j-1 > 0 )		
				oldImage.getPixelVal( i, j-1, N1 );
			if( i-1 > 0 && j-1 > 0 )
				oldImage.getPixelVal( i-1, j-1, N2 );
			if( i-1 > 0 )
				oldImage.getPixelVal( i-1, j, N3 );
			if( i-1 > 0 && j+1 < M ) // maybe M -1 ?
  				oldImage.getPixelVal( i-1, j+1, N4 );
			if( j+1 < M )
				oldImage.getPixelVal( i, j+1, N5 );
			if( i+1 < N && j+1 < M )
				oldImage.getPixelVal( i+1, j+1, N6 );
			if( i+1 < N ) 
				oldImage.getPixelVal( i+1, j, N7 );
			if( i+1 < N && j-1 > 0 )
				oldImage.getPixelVal( i+1, j-1, N8 );
			if( N1 == 255 || N2 == 255 || N3 == 255 || N4 == 255 || N5 == 255 || N6 == 255 || N7 == 255 || N8 == 255 )
				setPixelVal( i, j, 255 );
			else
				setPixelVal( i, j, val );
	}

}  

void ImageType::getSubImage( int& ULr, int& ULc, int& LRr, int& LRc, ImageType oldImage )
{
 int x = 0;
 int y = 0; 
 int i, j;
 int val;
 
 cout << "Enter upper left row: ";
 cin >> ULr; 
 cout << endl;

 cout << "Enter upper left column: ";
 cin >> ULc; 
 cout << endl;

 cout << "Enter lower right row: ";
 cin >> LRr;
 cout << endl;


 cout << "Enter lower right column: ";
 cin >> LRc;
 cout << endl;
 
 setImageInfo( (LRr-ULr), (LRc-ULc), oldImage.Q );
 

 for( i=ULr; i<LRr; i++, x++ )
    {
	y = 0;
    for( j=ULc; j<LRc; j++, y++) {
 	oldImage.getPixelVal( i, j, val);       
        setPixelVal( x, y, val);
	}
     }

}

void ImageType::rotateImage( float theta, ImageType oldImage ) 
{

 int i, j, val, x=0, y=0, i2=0, j2=0, i3 = 0, j3 = 0, d1, d2;
 float i0, j0;  
 int **holder;
 int size = (N*M);
 bool neg = true;
 int orgR = N;
 int orgC = M;

 // delete current pixel array
  for(i=0; i<N; i++) 
   delete [] pixelValue[i];
	delete [] pixelValue;


 // initialize holder array for new rotation coordinates
 holder = new int* [2];
 for( i = 0; i < 2; i++ ){
	holder[i] = new int [N*M];
	for( j = 0; j<(N*M); j++ ){
		holder[i][j] = 0;
}
}


 for( i=0; i<oldImage.N; i++, x++)
    {
	j = 0;
    for( j=0; j<oldImage.M; j++, y++)
	{

 	oldImage.getPixelVal( i, j, val);      
        i0 = 50 + ( i - 50 )*cos(theta)-(j-50 )*sin(theta);
        j0 = 50 + ( i - 50 )*sin(theta)+(j-50 )*cos(theta);
        

        i0 = floor( i0 + 0.5 );
        j0 = floor( j0 + 0.5 );

	holder[0][y] = i0;
	holder[1][y] = j0;
        if( i0 < i2 )
		i2 = i0;

	if( j0 < j2 )
		j2 = j0;
  
	if( i0 > i3 )
		i3 = i0;

	if( j0 > j3 )
		j3 = j0;
	
	}
}

i2 = i2 - (2*i2);
j2 = j2 - (2*j2);


	N = i2 + i3;
	M = j2 + j3;

  for( i = 0; i < (orgR*orgC); i++ ){
	if( holder[0][i] > 0 )
		holder[0][i] = holder[0][i]+(N-i3-1);

	if( holder[1][i] > 0 )
		holder[1][i] = holder[1][i]+(M-j3-1);

	}
	

  pixelValue = new int* [N];
  for( i = 0; i< N; i++ ){
 	pixelValue[i] = new int [M];
 	for( j = 0; j< M; j++ ){
		pixelValue[i][j] = 0;
	}
   }

	i2 = 0;
	j2 = 0;
	for( j = 0; j<(size); j++ ){
		if( holder[0][j] < i2 ) 
		  i2 = holder[0][j];

		if( holder[1][j] < j2 )
		  j2 = holder[1][j];
	}

	i3 = i2;
	j3 = j2;

	for( j = 0; j<(size); j++ ){
		if( holder[0][j] == i2) 
		  holder[0][j] = 0;

		if( holder[1][j] == j2)
		  holder[1][j] = 0;
		

	}
        i2 = 0;
	j2 = 0;
	
	while( neg == true )
	{
	neg = false;

	for( j = 0; j<(size); j++ ){
		if( holder[0][j] < 0 || holder[1][j] < 0 )
			neg = true;
	} 	
	
	if( neg == true ){
	for( j = 0; j<(size); j++ ){
		if( holder[0][j] < i2 ) 
		  i2 = holder[0][j];

		if( holder[1][j] < j2 )
		  j2 = holder[1][j];
	}
	d1 = i2 - i3;
	d2 = j2 - j3;
	for( j = 0; j<(size); j++ ){
		if( holder[0][j] == i2) 
		  holder[0][j] = d1;

		if( holder[1][j] == j2)
		  holder[1][j] = d2;
		
	}
	i2 = 0;
	j2 = 0;
	}
	}

	y= 0;
 for( i=0; i<orgR; i++)
    {
	j = 0;
    for( j=0; j<orgC; j++, y++)
	{
	oldImage.getPixelVal( i, j, val );
	setPixelVal( holder[0][y], holder[1][y], val );
	}

	}


for( i = 0; i<N; i++ ){
	j = 0;
  for( j=0; j<M; j++ ){
	if( pixelValue[i][j] == 0 && (j-1) > 0 && (j+1) < M ){
		if( pixelValue[i][j-1] > 0 && pixelValue[i][j+1] > 0 ){
			 setPixelVal( i, j, pixelValue[i][j-1] );
		}
		}
        
}
}

     
 for(i=0; i<2; i++) 
   delete [] holder[i];
	delete [] holder;
}

void ImageType::reflectImage( bool flag, ImageType oldImage)
{
int val;
int i, j, i2 = N, j2 = M; 

if( flag == true )
{
for( i = 0; i < N; i++ ){
for( j = 0; j < M; j++ ) 
{
oldImage.getPixelVal( (N-i-1), j, val );
setPixelVal( i, j, val );  

}	 
}

}

else if( flag == false )
{
for( j = 0; j < M; j++ ){
for( i = 0; i < N; i++ ) 
{
oldImage.getPixelVal( i, M-j-1, val );
setPixelVal( i, j, val );  

}	 
}

}

}

const ImageType operator +( ImageType &rhs, ImageType &lhs )
{

int i, j, r, c, q, q1, q2;

rhs.getImageInfo( r, c, q );
ImageType sum( r, c, q ); // use constructor


for( i = 0; i < r; i++ ){
for( j = 0; j < c; j++ )
{

rhs.getPixelVal( i, j, q );
lhs.getPixelVal( i, j, q1 );

q2 = (q + q1)/2;

sum.setPixelVal( i, j, q2 );


}
}

return sum;
}

const ImageType& ImageType :: operator=( const ImageType& rhs )
{
int i, j;

for( i = 0; i < N; i++ )
delete [] pixelValue[i];
delete [] pixelValue;

N = rhs.N;
M = rhs.M;
Q = rhs.Q;

pixelValue = new int* [N];
for( i = 0; i < N; i++ ){
pixelValue[i] = new int [M]; 
for( j = 0; j < M; j++ ){
pixelValue[i][j] = rhs.pixelValue[i][j];

}
}


return *this; 
}

void ImageType :: translateImage( int t, const ImageType& oldImage )
{
int i, j, i2, j2; 

for( i = N; i>0; i-- ){

i2 = i+t;

if( i2 < N )	
{							

for( j = M; j>0; j-- ){

j2 = j+t;

if( j2 < M )
pixelValue[i2][j2] = oldImage.pixelValue[i][j]; 
 

}
}

}

}

void ImageType::negativeImage(ImageType oldImage )
{
	int i, j, val;

   for( i=0; i<N; i++)
	{
	     
	   for( j=0; j<M; j++)
	   {	  
	     oldImage.getPixelVal(i, j, val);
	
	     val = 255-val;

	     setPixelVal(i, j, val);
	   }
	}
}

void ImageType :: enlargeImage ( int value, ImageType oldImage)
{

   int i, j, x, y, u, val, counter;
	
   for(i=0; i<N; i++) 
    delete [] pixelValue[i];
	delete [] pixelValue;

   N = value*N;
   M = value*M;

   pixelValue = new int* [N];
  for( i = 0; i< N; i++ ){
 	pixelValue[i] = new int [M];
 	for( j = 0; j< M; j++ ){
		pixelValue[i][j] = 0;
	}
   }


   for( i=0; i<N/value; i++)
	{	     
	   for( j=0; j<M/value; j++)
	   {	  	
	     oldImage.getPixelVal(i, j, val);
		x=value*j;
	     	y=value*i;
	for(int k=0; k<value; k++)
	{  u=x;
	for(int z=0; z<value; z++)
	  {  
	    setPixelVal(y, u, val);
		
	     u++;
	  }
	     y++;
	} 
	   }
	}

}



void ImageType :: shrinkImage ( float value, ImageType oldImage)
{
	
	int i, j, x=0, y=0, u, val, counter=0;

      for(i=0; i<N; i++) 
    delete [] pixelValue[i];
	delete [] pixelValue;

   N = (1/value)*N;
   M = (1/value)*M;

   pixelValue = new int* [N];
  for( i = 0; i< N; i++ ){
 	pixelValue[i] = new int [M];
 	for( j = 0; j< M; j++ ){
		pixelValue[i][j] = 0;
	}
   }

   for( i=0; i<N*value; i+=value)
	{
	   for( j=0; j<M*value; j+=value)
	   {
	     oldImage.getPixelVal(i, j, val);
		setPixelVal(y, x, val);
		x++;
	   }
	x=0;
	 y++;
	} 
}


int ImageType :: avgImage (ImageType oldImage)
{
	int i, j, val, h, counter=0, sum;

   for( i=0; i<N; i++)
	{
	     
	   for( j=0; j<M; j++)
	   {	  
	     oldImage.getPixelVal(i, j, val);
		counter++;
	     h = h+val;

	   }
	}
	sum=h/counter;
	return sum;
}


 void ImageType :: subImage ( ImageType oldImage, ImageType oldImage2)
{
	int i;
	int j;
	int val, h, sum;

	for( i=0; i<N; i++)
	{
	     
	   for( j=0; j<M; j++)
	   {
	    
	     oldImage.getPixelVal(i, j, val);
		h=val;

	     oldImage2.getPixelVal(i, j, val);	

	     sum = h-val;
	     if(sum<0)
		sum=sum*-1;
	     setPixelVal(i, j, sum);

	   }
	
	}

}


PixelType& PixelType :: operator = (const PixelType &rhs )
{

   x = rhs.x; 
   y = rhs.y;
   val = rhs.val;

   return *this;
}
   
void PixelType :: getValues( int& a, int& b, int& c )
{
   a = x;
   b = y;
   c = val;
}

void PixelType :: setValues( int a, int b, int c )
{
   x = a;
   y = b;
   val = c;
}

ostream& operator<< (ostream &out, PixelType &P )
{
out << P.val << ' ';

}

bool PixelType :: operator==( const PixelType &rhs) const
{
if( x == rhs.x && y == rhs.y && val == rhs.val)  
return true; 

else 
return false; 

}

bool PixelType :: operator!=( const PixelType &rhs) const
{
if( x != rhs.x || y != rhs.y || val != rhs.val)  
return true; 

else 
return false; 

}

RegionType& RegionType :: operator = (const RegionType &rhs )
{
pixels = rhs.pixels; 
centroid.x = rhs.centroid.x;
centroid.y = rhs.centroid.y;

return *this;
}

bool RegionType :: operator<( const RegionType &rhs) const
{
if( pixels.LengthIs() < rhs.pixels.LengthIs() )
return true;

else 
return false;

}
bool RegionType :: operator>( const RegionType &rhs) const
{

if( pixels.LengthIs() > rhs.pixels.LengthIs() )
return true;

else 
return false;


}
bool RegionType :: operator==( const RegionType &rhs) const
{

if( pixels.LengthIs() == rhs.pixels.LengthIs() )
return true;

else 
return false;

}

bool RegionType :: operator!=( const RegionType &rhs) const
{

if( pixels.LengthIs() != rhs.pixels.LengthIs() )
{
return true;
}

else
return false;

}

point& point :: operator = (const point &rhs )
{
x = rhs.x;
y = rhs.y;

return *this;
}

bool point :: operator == (const point &rhs ) const
{
return( x == rhs.x && y == rhs.y );

}

bool point :: operator != (const point &rhs ) const
{
return( x != rhs.x || y != rhs.y );

}
